路由的加载
简介
上一章,我们说过,在 Laravel 启动引导中,BootProviders
类的根本,就是调用 app.php
配置文件中服务类的 boot
方法。这一章我们就 App\Providers\RouteServiceProvider
这个服务类的 boot
方法进行解析。为什么要重点说这个类的 boot
方法呢,因为这个 boot
方法,就对我们在 web.php
或 api.php
中路由进行解析,并存入 Route
类服务实体中,最终绑定到 Application 容器实体中,为请求 URL 进行控制器和方法匹配,从而执行我们定义的方法。
正文
先看一下 App\Providers\RouteServiceProvider
的 boot
方法
public function boot()
{
//
parent::boot();
}
发现调用了父类的 boot
方法,那么我们去看父类
public function boot()
{
// 这一行,从词面不难看出是在设置根控制器命名空间,真实操作是设置 UrlGenerator 类对象中 rootNamespace 属性的值
$this->setRootControllerNamespace();
// 这一行是看 `./bootstrap/cache/routes.php` 缓存文件存在吗,存在的话就直接加载
if ($this->app->routesAreCached()) {
$this->loadCachedRoutes();
} else {
// **这个是路由加载的核心方法
$this->loadRoutes();
// 这一行,还记得上一中的 booted 的,就是全部服务类加载完后的操作;这里就是定义加载完后要执行的回调函数
$this->app->booted(function () {
$this->app['router']->getRoutes()->refreshNameLookups();
$this->app['router']->getRoutes()->refreshActionLookups();
});
}
}
我们接着看 $this->loadRoutes()
这个核心方法
protected function loadRoutes()
{
if (method_exists($this, 'map')) {
$this->app->call([$this, 'map']);
}
}
这段代码,从字面不难看出,就是判断当前对象有没有 map
方法,有的话就执行。注意,当前对象是 App\Providers\RouteServiceProvider
实例化的,所以我们要从这个服务类开看起,不应该直接去看它的父类。经过寻找,map
方法被发现
public function map()
{
$this->mapApiRoutes();
$this->mapWebRoutes();
//
}
哇,好熟悉,这不就是我们可以自行修改或添加自定义路由文件的方法吗。第一行加载 api.php
,第二行加载 web.php
我们以 api.php
来看
protected function mapApiRoutes()
{
Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
}
执行顺序:调用 Route
门面,从 route
解析出 Route
服务实体,然后调用 prefix
方法,这个方法实际是在 Route 实体对象 attributes 属性中设定 prefix => 'api'
数组键值对。然后调用 middleware 方法也是, namespace 方法也是,我们看一下结果图
最后 group
方法,会携带上面的 attributes 属性去执行 web.php 文件,如下
protected function loadRoutes($routes)
{ // $routes = 'D:/www/learn/routes/api.php'
if ($routes instanceof Closure) {
$routes($this);
} else {
$router = $this;
require $routes;
}
}
会包含执行 api.php,将路由绑定的 Route 服务实体中。